﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Security;
using System.Web.Mvc;

using Seven.Service.Security;
using Seven.Tools.Helper;
using Seven.Tools.Extension;

namespace Seven.Site.Mvc.Filters
{
    /// <summary>
    /// 定义一个用于刷新 ASP.NET MVC 登录用户登录身份 Token 状态的过滤器。
    /// </summary>
    public class UpdateFormsAuthenticationAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// 在 Action 执行之前操作，刷新用户登录身份的状态。
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (Seven.Service.SiteCore.UseRemotePool)
            {
                UserInfoModel userInfo = null;
                LoginedModel model = LoginModelUtility.GetLoginModel<UserInfoModel>(filterContext.HttpContext, out userInfo);
                if (model != null && model.Token != default(Guid))
                {
                    int timeout = ServicePool.UpdateTokenLastRequestTime(model);

                    LoginModelUtility.SetLoginModel(filterContext.HttpContext, userInfo, timeout);
                }
            }
            else
            {
                UserInfoModel userInfo = null;
                LoginedModel model = LoginModelUtility.GetLoginModel<UserInfoModel>(filterContext.HttpContext, out userInfo);
                LoginModelUtility.SetLoginModel(filterContext.HttpContext, userInfo, Seven.Service.SiteCore.SessionTimeout);
            }
        }
    }

    /// <summary>
    /// 用于验证登录身份信息是否有效。
    /// </summary>
    public class LoginModelUtility
    {
        #region 获取和设置 ASP.NET MVC 请求中的登录用户身份信息

        /// <summary>
        /// 获取 ASP.NET MVC 过滤器拦截到请求中的用户登录身份验证信息。
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="httpContext"></param>
        /// <param name="resultModel"></param>
        /// <returns></returns>
        public static LoginedModel GetLoginModel<TResult>(HttpContextBase httpContext, out TResult resultModel) where TResult : class,new()
        {
            if (httpContext != null && httpContext.Request != null && httpContext.Request.Cookies != null)
            {
                HttpCookie cookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
                try
                {
                    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
                    resultModel = JsonHelper.DeserializeObject<TResult>(ticket.UserData);
                    return resultModel.CastTo<LoginedModel>();
                }
                catch
                {
                    new Exception("无法从FormsAuthenticationTicket中获取当前登录信息，更新FormsAuthenticationTicket失败。");
                }
            }
            resultModel = null;
            return null;
        }

        /// <summary>
        /// 设置 ASP.NET MVC 中的用户登录身份验证信息。
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="model"></param>
        /// <param name="timeout"></param>
        public static void SetLoginModel(HttpContextBase httpContext, UserInfoModel model, int timeout)
        {
            if (httpContext != null && httpContext.Response != null && httpContext.Response.Cookies != null && model != null)
            {
                var response = httpContext.Response;
                if (response == null || response.Cookies == null || model == null)
                { return; }

                string modelText = JsonHelper.SerializeObject(model);

                DateTime expiration = timeout == 0 ? DateTime.Now.AddYears(10) : DateTime.Now.AddMinutes(timeout);

                FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, model.UserName, DateTime.Now, expiration, false, modelText);
                string encrptyTicket = FormsAuthentication.Encrypt(ticket);

                HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encrptyTicket);
                cookie.Expires = ticket.Expiration;

                response.Cookies.Set(cookie);
            }
        }

        #endregion
    }
}
